/*-+-*****************************************************************************
 Copyright (C) 2009-2012 Oceanside Software Corporation (R)
 Dallas, Texas

 Author       : Jason T. Brower
 Purpose      : This file is part of a point-of-sale software package
                originally written for Table-Service Restaurants.
              
 Date         : July 8th of 2012
              
 History      : The application that this software file belongs to was 
                written over a 3-Year period.  The first version was written
                in C# .Net 3.5 using Windows Forms.  Being new to .Net at
                the time, I soon learned of Windows Presentation Foundation,
                liked the graphics capabilities and databinding so I
                migrated to WPF.  When the .Net 4.0 framework was released
                the application was migrated from 3.5 to 4.0. Now today,
                .Net 4.5 RC is out and I was already in process of migrating
                before I made this public.
                
Excuse Me     : Please excuse the coding style, it is not representative of
                my typical work.  During the three years I was under a very
                tight budget, constantly running out of funding and was 
                working 18 hour days hyped up on 5-Hour Energy.  The 
                design is not textbook.  It is monolythic because of
                previous issues I will not get into.  There are few comments
                in the code and the ones that are there may be incorrect.
                
What it is not: This is not the exact version that I took through PA-DSS 2.0
                nor is it the exact version running at my customer sites.
                I removed some details for security purposes including some
                encryption algorithms.  The reporting was removed because
                I was including a third party library that cannot be 
                published with this Open Source.
                
What it is    : This application has been running at high volume restaraunts
                for over a year.  It never freezes, crashes or does anything
                unexpected.  So long as it is built, installed and configured
                correctly, it is a strong system that we can all enhance.

Contact       : Please note that I can no longer work for free.  However, you
                can reach me at information@oceansidesw.com.                                                      

*********************************************************************************/
using System.Collections.Generic;
using System.Linq;
using System;
#if _LOCALDB
  using Oceanside.LocalDB;
#else
using Oceanside.RemoteDB;
#endif

#if _LOCALDB
  namespace Oceanside.LocalDB
#else
namespace Oceanside.RemoteDB
#endif
{
    public partial class PAYROLL_RUN
    {
        public delegate void RunPayrollCallback(Run prun, bool wasInOldPayrollError);
        public delegate void BindPayrollPeriodHistoryWindowCallBack(List<PAYROLL_RUN> runs);

        public class Run
        {
            public List<PayType> UserPayObjectList = new List<PayType>();
        }

        public class PayType
        {
            Int64 user;
            public Int64 UserId
            {
                get { return user; }
                set { user = value; }
            }

            string userName;
            public string FullName
            {
                get { return userName; }
                set { userName = value; }
            }

            JOB_CODE jobCode;
            public JOB_CODE JobCode
            {
                get { return jobCode; }
                set { jobCode = value; }
            }

            Int64 roleId;
            public Int64 RoleId
            {
                get { return roleId; }
                set { roleId = value; }
            }

            decimal payRate;
            public decimal PayRate
            {
                get { return payRate; }
                set { payRate = value; }
            }

            int totalMinutes;
            public int TotalMinutes
            {
                get { return totalMinutes; }
                set { totalMinutes = value; }
            }

            public decimal TotalHours
            {
                get
                {
                    decimal hours = (totalMinutes / 60M);
                    if (hours != 0)
                    {
                        return Math.Round(hours, 2);
                    }
                    return 0M;
                }
            }

            public decimal GrossPay
            {
                get
                {
                    if (TotalHours != 0)
                    {
                        decimal pay = TotalHours * payRate;
                        return Math.Round(pay, 2);
                    }
                    return 0M;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static PAYROLL_RUN GetLastPayrollRun()
        {
            PAYROLL_RUN pr = null;
            using (DominicaEntities context = new DominicaEntities(DominicaEntities.GetEfConString()))
            {
                var q = from PAYROLL_RUN in context.PAYROLL_RUNS orderby PAYROLL_RUN.EndDate descending select PAYROLL_RUN;
                if (q.Any())
                {
                    pr = q.First();
                }
            }
            return pr;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static void GetLastPayrollRuns(int takeCount, BindPayrollPeriodHistoryWindowCallBack callback)
        {
            List<PAYROLL_RUN> runList = null;
            try
            {
                using (DominicaEntities context = new DominicaEntities(DominicaEntities.GetEfConString()))
                {
                    var q = from PAYROLL_RUN in context.PAYROLL_RUNS orderby PAYROLL_RUN.EndDate descending select PAYROLL_RUN;
                    if (q.Any())
                    {
                        runList = q.Take(takeCount).ToList();
                    }
                }
            }
            catch (Exception e)
            {
                Constants.ErrorDispatcher.Notify(Error.EType.E_REPORT_ERROR, e);
            }
            finally
            {
                try
                {

                    PosWindow.MainPosWindow.Dispatcher.Invoke(callback, runList);
                }
                catch (Exception e)
                {
                    Logging.LogException(e);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="timePunches"></param>
        /// <returns></returns>
        public static Run RunPayroll(List<TIMESLIP> tp, RunPayrollCallback callBack, DateTime startDate, DateTime endDate)
        {
            Run run = new Run();
            List<TIMESLIP> timePunches = tp;
            bool isDataFetchOnly = tp == null;
            bool periodAlreadyRun = false;
            try
            {
                using (DominicaEntities context = new DominicaEntities(DominicaEntities.GetEfConString()))
                {
                    ///First off, let's make sure we have not already run this payroll period before.
                    PAYROLL_RUN lastPrun = null;
                    if (!isDataFetchOnly)
                    {
                        lastPrun = PAYROLL_RUN.GetLastPayrollRun();
                    }

                    if (!isDataFetchOnly && (lastPrun != null && startDate <= lastPrun.EndDate))
                    {
                        periodAlreadyRun = true;
                    }
                    else
                    {
                        if (tp == null)
                        {
                            context.TIMESLIPS.MergeOption = System.Data.Objects.MergeOption.NoTracking;
                            var qTp = from TIMESLIP in context.TIMESLIPS
                                      where TIMESLIP.ClockedOut >= startDate
                                      && TIMESLIP.ClockedOut <= endDate
                                      select TIMESLIP;

                            if (qTp.Any())
                            {
                                timePunches = qTp.ToList();
                            }
                        }

                        ///Get all of the unique users.
                        var q = timePunches.Select(x => x.UserId).Distinct();

                        if (q.Any())
                        {
                            ///Pull the unique user list so we dont get open query error.
                            List<Int64> uniqueUsersList = q.ToList();

                            ///Loop through every user in the list.
                            foreach (Int64 userId in uniqueUsersList)
                            {
                                ///Grab all timepunches for this user.
                                var q2 = from TIMESLIP in timePunches where TIMESLIP.UserId == userId select TIMESLIP;
                                if (q2.Any())
                                {
                                    ///Put the timepunches for this user in a list so we do not get an open query error.
                                    List<TIMESLIP> usersTimepunches = q2.ToList();

                                    ///Get all unique ROLES for this user so we can sort by jobcode.
                                    var q3 = usersTimepunches.Select(r => r.RoleId).Distinct();
                                    if (q3.Any())
                                    {
                                        ///Put the ROLES into a list so we don't get an open query error.
                                        List<Int64> rolesList = q3.ToList();
                                        foreach (Int64 roleId in rolesList)
                                        {
                                            PayType userPayObject = new PayType();
                                            userPayObject.UserId = userId;
                                            userPayObject.FullName = context.USERS.Where(x => x.Id == userId).Select(x => x.FirstName + " " + x.LastName).First();
                                            userPayObject.RoleId = roleId;
                                            userPayObject.PayRate =
                                                context.ROLES.Include("PAYGRADE").Where(r => r.Id == roleId).First().PAYGRADE.WageRate;
                                            userPayObject.JobCode =
                                                context.ROLES.Include("JOB_CODE").Where(r => r.Id == roleId).First().JOB_CODE;

                                            ///Sum all hours for this user on each jobcode.
                                            List<TimeSpan> timeSpans =
                                                usersTimepunches.Where(x => x.RoleId == roleId).Select(x => (TimeSpan)(x.ClockedOut - x.ClockedIn)).ToList();
                                            TimeSpan total = timeSpans.Aggregate(TimeSpan.Zero, (sum, value) => sum.Add(value));
                                            userPayObject.TotalMinutes = (int)total.TotalMinutes;

                                            if (userPayObject.TotalMinutes >= 1)
                                            {
                                                run.UserPayObjectList.Add(userPayObject);
                                            }
                                        }
                                    }
                                }
                            }
                        }

                        if (run.UserPayObjectList.Count() != 0 && !isDataFetchOnly)
                        {
                            ///Add the new payroll run.
                            PAYROLL_RUN payrollRun = new PAYROLL_RUN();
                            payrollRun.StartDate = startDate;
                            payrollRun.EndDate = endDate;
                            context.PAYROLL_RUNS.AddObject(payrollRun);
                            context.SaveChanges();
                        }
                    }
                }
                PosWindow.MainPosWindow.Dispatcher.Invoke(callBack, run, periodAlreadyRun);
            }
            catch (Exception e)
            {
                Constants.ErrorDispatcher.Notify(Error.EType.E_REPORT_ERROR, e);
            }
            return run;
        }
    }
}
